package jsprintorbitor.experiment;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

import laboratoryQ.computation.flatness.DiagramData;
import laboratoryQ.computation.flatness.FlatnessUtils;
import laboratoryQ.computation.flatness.Point;
import laboratoryQ.computation.flatness.RangeInfo;
import laboratoryQ.computation.flatness.SemanticDiagramData;
import laboratoryQ.computation.flatness.transform.BoardTransformer;
import laboratoryQ.computation.flatness.transform.ConstantBoardInfo;
import laboratoryQ.draw.DiagramDrawer;
import laboratoryQ.draw.IDrawer;
import laboratoryQ.experiment.IExperimentResult;
import laboratoryQ.experiment.reportage.ui.DrawingJComponent;
import laboratoryQ.experiment.reportage.ui.diagram2component.IDiagramsExtractor;
import laboratoryQ.experiment.reportage.ui.diagram2component.ILegendExtractor;
import laboratoryQ.experiment.reportage.ui.diagram2component.ITitlesExtractor;
import laboratoryQ.style.IStylizer;

public class SpOrbDiaTtlExtractor implements IDiagramsExtractor, ITitlesExtractor, ILegendExtractor {
	private static final RangeInfo SIN_DIA_RANGE_INFO = new RangeInfo(0, 2 * Math.PI, -1, 1);;
	private static final DiagramData SIN_DIA_DATA;
	static {
		int n = 100;
		SIN_DIA_DATA = new DiagramData(n);
		double step = Math.PI * 2 / n;
		double x = 0;
		for (int i = 0; i < n; i++) {
			x += step / 2;
			SIN_DIA_DATA.x[i] = x;
			SIN_DIA_DATA.y[i] = -Math.sin(x);
			x += step / 2;
		}
	}

	private IStylizer stylizer = IStylizer.EMPTY_INSTANCE;

	@Override
	public List<String> getTitles(IExperimentResult expResult) {
		if (expResult == null) {
			return Collections.emptyList();
		}
		SpOrbExperimentContext context = (SpOrbExperimentContext) expResult;
		List<String> result = new ArrayList<String>();
		for (SpOrbExperimentTimeMearurement tmMes : context.getTimeMearurements()) {
			result.add(tmMes.getTitle());
		}
		return result;
	}

	@Override
	public List<DiagramData[]> extractDiagrams(IExperimentResult expResult) {
		if (expResult == null) {
			return Collections.emptyList();
		}
		SpOrbExperimentContext context = (SpOrbExperimentContext) expResult;
		List<DiagramData[]> result = new ArrayList<DiagramData[]>();
		for (SpOrbExperimentTimeMearurement tmMes : context.getTimeMearurements()) {
			result.add(tmMes.getDiagrams());
		}
		return result;
	}

	@Override
	public List<String> getXCaptions(IExperimentResult expResult) {
		return genCaptList("gr. size", expResult);
	}

	@Override
	public List<String> getYCaptions(IExperimentResult expResult) {
		return genCaptList("comp. time", expResult);
	}

	@Override
	public List<Point> extractAxesStartPoints(IExperimentResult expResult) {
		if (expResult == null) {
			return Collections.emptyList();
		}
		SpOrbExperimentContext context = (SpOrbExperimentContext) expResult;
		List<Point> result = new ArrayList<Point>();
		for (SpOrbExperimentTimeMearurement tmMes : context.getTimeMearurements()) {
			RangeInfo rangeInfo =
				FlatnessUtils.getDiagramsRange(Arrays.asList(tmMes.getDiagrams()));
			rangeInfo.minY = Math.min(rangeInfo.minY, 0);
			result.add(new Point(rangeInfo.minX, rangeInfo.minY));
		}
		return result;
	}

	// TODO: switch-off legend on fail
	@Override
	public List<JComponent> extractLegends(IExperimentResult expResult) {
		if (expResult == null) {
			return Collections.emptyList();
		}
		SpOrbExperimentContext context = (SpOrbExperimentContext) expResult;
		List<JComponent> result = new ArrayList<JComponent>();

		for (SpOrbExperimentTimeMearurement tmMes : context.getTimeMearurements()) {
			JPanel legendPanel = new JPanel();
			legendPanel.setLayout(new GridBagLayout());
			GridBagConstraints gbc = new GridBagConstraints();

			gbc.gridx = 0;
			gbc.gridy = 0;
			gbc.weightx = 1;
			gbc.weighty = 0.5;
			gbc.gridwidth = 2;
			gbc.gridheight = 1;
			gbc.fill = GridBagConstraints.VERTICAL;
			gbc.anchor = GridBagConstraints.CENTER;
			legendPanel.add(new JLabel(tmMes.getTitle()), gbc);

			gbc.weighty = 1;
			gbc.gridwidth = 1;
			gbc.gridy++;
			for (int i = 0; i < tmMes.getDiagrams().length; i++) {
				Object semantic = null;
				boolean semanticDiscovered = false;
				DiagramData origDia = tmMes.getDiagrams()[i];
				if (origDia instanceof SemanticDiagramData) {
					semantic = ((SemanticDiagramData) origDia).getSemantic();
					semanticDiscovered = true;
				}

				DiagramData diagram = semanticDiscovered ?
						new SemanticDiagramData(SIN_DIA_DATA.x, SIN_DIA_DATA.y, semantic) :
						SIN_DIA_DATA;
				IDrawer drawer = new DiagramDrawer(
						diagram,
						new BoardTransformer(new ConstantBoardInfo(5), SIN_DIA_RANGE_INFO, true)
				);
				DrawingJComponent sampleComp = new DrawingJComponent(drawer, stylizer);
				sampleComp.setPreferredSize(new Dimension(50, 30));
				gbc.fill = GridBagConstraints.BOTH;
				gbc.anchor = GridBagConstraints.CENTER;
				legendPanel.add(sampleComp, gbc);

				gbc.gridx++;
				gbc.fill = GridBagConstraints.NONE;
				gbc.anchor = GridBagConstraints.WEST;
				JLabel infoLabel = new JLabel(
						semanticDiscovered ? "" + semantic : 
						"Diagram " + i);
				legendPanel.add(infoLabel, gbc);
				gbc.gridx = 0;
				gbc.gridy++;
			}

			legendPanel.setPreferredSize(new Dimension(250, 50 * (tmMes.getDiagrams().length + 1)));
			result.add(legendPanel);
		}
		return result;
	}
	
	public void setStylizer(IStylizer stylizer) {
		this.stylizer = stylizer;
	}

	private List<String> genCaptList(String caption, IExperimentResult expResult) {
		if (expResult == null) {
			return Collections.emptyList();
		}
		SpOrbExperimentContext context = (SpOrbExperimentContext) expResult;
		int count = context.getTimeMearurements().size();
		List<String> result = new ArrayList<String>();
		for (int i = 0; i < count; i++) {
			result.add(caption);
		}
		return result;
	}
}